import openpyxl
from tools.handle_loguru import logger
from tools.handle_path import testCase_xlsx_file
from conf.conf import SHEET


# 封装一个xlsx表格操作类
class HandleExcel:

    def __init__(self, excel_path: str):
        """
        传入一个xlsx文件路径，用load_workbook()方法加载，如果文件加载不成功，抛出异常。如果成功，打开一个工作簿。
        :param excel_path: xlsx文件路径
        """
        logger.info("========= 当前打开的excel文件为:{} =========".format(excel_path))
        try:
            self.wb = openpyxl.load_workbook(excel_path)
        except:
            logger.exception("excel文件加载失败！")
            raise

    def select_sheet_by_name(self, sheet_name: str):
        """
        根据传入的工作表名字，打开工作表。
        :param sheet_name: 工作表的名字
        """
        self.sh = self.wb[sheet_name]
        logger.info("========= 当前正在操作的表单名是:{} =========".format(sheet_name))

    def close_workbook(self):
        """
        关闭当前打开的工作簿
        """
        self.wb.close()
        logger.info("========= 工作簿已关闭 =========")

    def read_all_rows_data_by_column_num(self, start_col_num=SHEET.get('start_col_num'), end_col_num=SHEET.get('end_col_num')):
        """
        指定列的范围获取数据，组成测试用例数据。默认是第1列到第11列。封装此方法的目的是避免有多余的空单元格。
        :param start_col_num: 列的起始位置，在conf.py文件中配置，比如："start_col_num": 1
        :param end_col_num: 列的结束位置，在conf.py文件中配置，比如："end_col_num": 11
        :return:返回指定列范围内组成的测试用例数据，字典类型。
        """

        if start_col_num and end_col_num:
            logger.info(f'========= 指定表单列的范围({start_col_num}~{end_col_num})读取数据 =========')
            # 步骤1、获取第一行的数据
            keys = []
            for row in self.sh.iter_rows(min_row=1, max_row=1, min_col=start_col_num, max_col=end_col_num):
                for cell in row:
                    keys.append(cell.value)
            logger.debug(f'表单第一行的数据 类型为：{type(keys)}，数据内容为：{keys}')

            # 步骤2、获取第1行以后的每一行数据和第1行组成字典
            # sh.iter_rows(min_row=2, max_col=11) 表示：列的范围为：1 ~ 11列，行的范围为：2 ~ 无穷大。
            all_cases_datas_list = []  # 定义一个存放所有测试用例的列表
            for row in self.sh.iter_rows(min_row=2, max_col=end_col_num):
                after_first_single_row_data = []  # 定义一个列表，存放(2~无穷大)行的数据
                for cell in row:
                    after_first_single_row_data.append(cell.value)  # 把每个单元格的数据添加到行列表中
                one_case_dict = dict(zip(keys, after_first_single_row_data))  # 第一行数据和(2~无穷大)行组成字典，形成一条测试用例。

                # 步骤3、把每一条测试用例存放到所有测试用例列表中
                all_cases_datas_list.append(one_case_dict)

            logger.debug(f'excel表单中的数据拼接后，数据类型为：{type(all_cases_datas_list)}，数据内容为：{all_cases_datas_list}')
            return all_cases_datas_list
        else:
            logger.info(f'========= 列范围的 起始值 或 结束值为0，不符合要求。 =========')

    def read_all_rows_data(self):
        """
        不指定列的范围获取数据，组成测试用例数据。建议用read_all_rows_data_by_column_num()方法，此方法较少用。
        :return: 测试用例数据列表
        """
        # 获取表单的所有行，即获取表单的所有数据
        #  步骤1、从选定的表单当中，第一行作为key。
        #  步骤2、将后面的每一行数据，与第一行拼接成一个字典数据，作为一条测试用例数据。
        #  步骤3、将所有测试用例数据，添加到一个列表当中。

        logger.info(f'========= 获取表单的所有行，即获取表单的所有数据。 =========')
        sheet_all_rows = list(self.sh.values)
        logger.debug(f'表单的所有数据 类型为：{type(sheet_all_rows)}，数据内容为：{sheet_all_rows}')

        # 把第一行作为数据的keys
        keys = sheet_all_rows[0]
        logger.debug(f'表单第一行的数据 类型为：{type(keys)}，数据内容为：{keys}')

        # 列表变量 - 存放表单当中的每一行数据
        all_cases_datas_list = []

        # 以下代码功能：excel表单第2行开始的每一行测试数据，与第一行的keys拼接成一个字典。
        for single_row in sheet_all_rows[1:]:
            one_case_dict = dict(zip(keys, single_row))
            all_cases_datas_list.append(one_case_dict)
        logger.debug(f'excel表单中的数据拼接后，数据类型为：{type(all_cases_datas_list)}，数据内容为：{all_cases_datas_list}')
        return all_cases_datas_list


# 只打开一次测试用例表格，所以做成单例模式
handle_excel = HandleExcel(testCase_xlsx_file)

# 测试代码
if __name__ == '__main__':
    from tools.handle_path import testCase_xlsx_file
    he = HandleExcel(testCase_xlsx_file)
    he.select_sheet_by_name('verification_code_message')
    he.read_all_rows_data_by_column_num()
    he.close_workbook()
